package com.njtoyo.taxi.admin.service.data.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.njtoyo.taxi.admin.handler.FilterHandler;
import com.njtoyo.taxi.admin.mapper.master.entity.*;
import com.njtoyo.taxi.admin.mapper.master.enums.RideOrderState;
import com.njtoyo.taxi.admin.mapper.master.enums.RideStateState;
import com.njtoyo.taxi.admin.mapper.master.enums.SimpleCallRideState;
import com.njtoyo.taxi.admin.mapper.master.mapper.*;
import com.njtoyo.taxi.admin.mapper.secondary.entity.data.DashboardCallDailyInfo;
import com.njtoyo.taxi.admin.mapper.secondary.entity.data.DashboardDriverServiceDaily;
import com.njtoyo.taxi.admin.mapper.secondary.entity.data.DashboardOrderDaily;
import com.njtoyo.taxi.admin.mapper.secondary.enums.DataOrderType;
import com.njtoyo.taxi.admin.mapper.secondary.mapper.DashboardDriverServiceDailyMapper;
import com.njtoyo.taxi.admin.mapper.secondary.mapper.data.DashboardCallDailyInfoMapper;
import com.njtoyo.taxi.admin.mapper.secondary.mapper.data.DashboardOrderDailyMapper;
import com.njtoyo.taxi.admin.mapper.secondary.mapper.data.FunnelMapper;
import com.njtoyo.taxi.admin.rest.pojo.dto.funnnel.FunnelCallVO;
import com.njtoyo.taxi.admin.rest.presenter.data.funnel.CancelDataVO;
import com.njtoyo.taxi.admin.rest.presenter.data.funnel.OrderCallChartVO;
import com.njtoyo.taxi.admin.rest.presenter.data.funnel.TodayStatisticVO;
import com.njtoyo.taxi.admin.rest.presenter.platfrom.AdminUserIdentityPresenter;
import com.njtoyo.taxi.admin.rest.wrapper.data.funnel.FunnelWrapper;
import com.njtoyo.taxi.admin.service.data.FunnelService;
import com.njtoyo.taxi.admin.utils.RAMPageUtil;
import com.njtoyo.taxi.entity.backend.AdminUser;
import com.taxi.entity.common.RestResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;


/**
 * @author mdf
 * @title: FunnelServiceImpl
 * @description: TODO
 * @date 2024/1/3 10:00
 */
@Service
public class FunnelServiceImpl implements FunnelService {

    @Autowired
    private FunnelMapper funnelMapper;
    @Autowired
    private RideMapper rideMapper;
    @Autowired
    private SimpleCallRideMapper simpleCallRideMapper;
    @Autowired
    private DriverConfirmCallPersistedMapper driverConfirmCallPersistedMapper;
    @Autowired
    private RideOrderMapper rideOrderMapper;
    @Autowired
    private SimpleCallRideOrderMapper simpleCallRideOrderMapper;
    @Autowired
    private DriverServiceStatisticsMapper driverServiceStatisticsMapper;
    @Autowired
    private TaxiMapper taxiMapper;
    @Autowired
    private DashboardCallDailyInfoMapper dashboardCallDailyInfoMapper;
    @Autowired
    private DashboardOrderDailyMapper dashboardOrderDailyMapper;
    @Autowired
    private DashboardDriverServiceDailyMapper dashboardDriverServiceDailyMapper;
    @Autowired
    private RealtimeCallMapper realtimeCallMapper;
    @Autowired
    private DriverGrabRealtimePersistedMapper driverGrabRealtimePersistedMapper;
    @Autowired
    private DriverMapper driverMapper;

    @Override
    public RestResult<TodayStatisticVO> todayStatistic(AdminUser adminUser, FunnelWrapper wrapper) {
        FilterHandler filterHandler = new FilterHandler(adminUser, wrapper.getCantonId(), wrapper.getCompanyId());
        wrapper.setStartDate(DateUtil.beginOfDay(DateUtil.date()));
        wrapper.setEndDate(DateUtil.endOfDay(DateUtil.date()));
        TodayStatisticVO todayStatisticVO = new TodayStatisticVO();
        //今日乘客数据
        getPassengerStatistic(todayStatisticVO, wrapper);
        //司机部分
        getDriverStatistic(todayStatisticVO, wrapper);


        return RestResult.success(todayStatisticVO);
    }


    private void getDriverStatistic(TodayStatisticVO todayStatisticVO, FunnelWrapper wrapper) {
        List<Driver> drivers = funnelMapper.getDriverToday(wrapper);
        if (CollUtil.isNotEmpty(drivers)) {
            List<Long> driverIds = drivers.stream().map(Driver::getId).collect(Collectors.toList());

            Integer countOutCar = driverServiceStatisticsMapper.selectCount(Wrappers.<DriverServiceStatistics>lambdaQuery().in(DriverServiceStatistics::getDriverId, driverIds).between(DriverServiceStatistics::getCreatedAt, wrapper.getStartDate(), wrapper.getEndDate()));


            List<DriverConfirmCallPersisted> driverConfirmCallPersisteds = driverConfirmCallPersistedMapper.selectList(Wrappers.<DriverConfirmCallPersisted>lambdaQuery().between(DriverConfirmCallPersisted::getCreatedAt, wrapper.getStartDate(), wrapper.getEndDate()).in(DriverConfirmCallPersisted::getDriverId, driverIds).groupBy(DriverConfirmCallPersisted::getDriverId));
            //应答
            Integer countReply = funnelMapper.selecDriverCountReply(wrapper);
            //实时出车数
            Integer outCarNum = taxiMapper.selectCount(Wrappers.<Taxi>lambdaQuery().in(Taxi::getIsAvailable, 1));


            //累计应答
            todayStatisticVO.setDriverCountReply(countReply);
            //累计出车
            todayStatisticVO.setCountOutCar(countOutCar);
            //累计通知
            todayStatisticVO.setDriverCountNotify(driverConfirmCallPersisteds.size());
            todayStatisticVO.setCountOutCar(outCarNum);

        }


    }

    /**
     * 乘客数据概览统计
     *
     * @param todayStatisticVO
     * @param wrapper
     */
    private void getPassengerStatistic(TodayStatisticVO todayStatisticVO, FunnelWrapper wrapper) {
        List<FunnelCallVO> callList = funnelMapper.getTodayCall(wrapper);

        Integer countReply = driverConfirmCallPersistedMapper.selectCount(Wrappers.<DriverConfirmCallPersisted>lambdaQuery().between(DriverConfirmCallPersisted::getCreatedAt, wrapper.getStartDate(), wrapper.getEndDate()));
        //行程费用
        BigDecimal rideAmount = funnelMapper.selectRideAmountCount(wrapper);
        //佣金
        BigDecimal serviceAmount = funnelMapper.selectServiceAmountCount(wrapper);
        //完单数
        Integer completeOrder = funnelMapper.getCompleteOrderNum(wrapper);


        //累计呼叫
        todayStatisticVO.setCountCall(CollUtil.isNotEmpty(callList) ? callList.size() : 0);
        todayStatisticVO.setCountReply(countReply);
        todayStatisticVO.setCountRideAmount(rideAmount);
        todayStatisticVO.setCountServiceAmount(serviceAmount);
        todayStatisticVO.setCompleteOrder(completeOrder);

    }


    /**
     * @param adminUser
     * @param funnelWrapper
     * @return
     */
    @Override
    public RestResult<?> orderChart(AdminUser adminUser, Integer type, FunnelWrapper funnelWrapper) {
        List<OrderCallChartVO> callChartVOS = new ArrayList<>();
        if (type == 1) {
            List<DashboardCallDailyInfo> list = dashboardCallDailyInfoMapper.selectList(Wrappers.<DashboardCallDailyInfo>lambdaQuery().between(DashboardCallDailyInfo::getCreateDate, DateUtil.formatDate(DateUtil.offsetDay(DateUtil.date(), -30)), DateUtil.formatDate(DateUtil.date())).orderByDesc(DashboardCallDailyInfo::getCreateDate));
            for (DashboardCallDailyInfo entity : list) {
                callChartVOS.add(new OrderCallChartVO().setType("呼叫数").setNum(entity.getCallNum()).setCreateDate(entity.getCreateDate()));
                callChartVOS.add(new OrderCallChartVO().setType("完单数").setNum(entity.getCompleteNum()).setCreateDate(entity.getCreateDate()));
            }
        } else {
            List<DashboardOrderDaily> list = dashboardOrderDailyMapper.selectList(Wrappers.<DashboardOrderDaily>lambdaQuery().between(DashboardOrderDaily::getStatsDate, DateUtil.formatDate(DateUtil.offsetDay(DateUtil.date(), -30)), DateUtil.formatDate(DateUtil.date())).orderByAsc(DashboardOrderDaily::getStatsDate));
            Map<String, List<DashboardOrderDaily>> group = list.stream().collect(Collectors.groupingBy(DashboardOrderDaily::getStatsDate));

            List<Map.Entry<String, List<DashboardOrderDaily>>> listMap = new ArrayList<Map.Entry<String, List<DashboardOrderDaily>>>(group.entrySet());
            Collections.sort(listMap, new Comparator<Map.Entry<String, List<DashboardOrderDaily>>>() {
                //升序排序
                public int compare(Map.Entry<String, List<DashboardOrderDaily>> o1, Map.Entry<String, List<DashboardOrderDaily>> o2) {
                    return o1.getKey().compareTo(o2.getKey());
                }
            });
            for (Map.Entry<String, List<DashboardOrderDaily>> entry : listMap) {

                Long noResponseReal = entry.getValue().stream().filter(entity -> entity.getType() == DataOrderType.noResponseReal).collect(Collectors.counting());
                Long orderCancel = entry.getValue().stream().filter(entity -> entity.getType() == DataOrderType.orderCancel).collect(Collectors.counting());
                callChartVOS.add(new OrderCallChartVO().setType("无应答订单").setNum(noResponseReal == null ? 0 : noResponseReal.intValue()).setCreateDate(entry.getKey()));
                callChartVOS.add(new OrderCallChartVO().setType("取消订单").setNum(orderCancel == null ? 0 : orderCancel.intValue()).setCreateDate(entry.getKey()));
            }
        }
        return RestResult.success(callChartVOS);
    }


    /**
     * 抢单变化表
     *
     * @param adminUser
     * @param funnelWrapper
     * @return
     */
    @Override
    public RestResult<?> get30DayGrabOrder(AdminUser adminUser, FunnelWrapper funnelWrapper) {
        List<DashboardCallDailyInfo> list = dashboardCallDailyInfoMapper.selectList(Wrappers.<DashboardCallDailyInfo>lambdaQuery().between(DashboardCallDailyInfo::getCreateDate, DateUtil.formatDate(DateUtil.offsetDay(DateUtil.date(), -30)), DateUtil.formatDate(DateUtil.date())).orderByAsc(DashboardCallDailyInfo::getCreateDate));
        List<OrderCallChartVO> grabOrders = new ArrayList<>();
        for (DashboardCallDailyInfo entity : list) {
            grabOrders.add(new OrderCallChartVO().setType("抢单通知次数").setNum(entity.getSendDriverNum() == null ? 0 : entity.getSendDriverNum()).setCreateDate(entity.getCreateDate()));
            grabOrders.add(new OrderCallChartVO().setType("参与抢单司机数").setNum(entity.getGrabCallNum() == null ? 0 : entity.getGrabCallNum()).setCreateDate(entity.getCreateDate()));
        }
        Date startDate = DateUtil.beginOfDay(DateUtil.date());
        Date endDate = DateUtil.endOfDay(DateUtil.date());

        List<OrderCallChartVO> inServiceList = dashboardDriverServiceDailyMapper.selectInServiceChart(DateUtil.offsetDay(startDate, -30), DateUtil.offsetDay(endDate, -1));

        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put("grabOrders", grabOrders);
        resultMap.put("inService", inServiceList);

        return RestResult.success(resultMap);
    }

    @Override
    public RestResult<?> getOrderData(AdminUser adminUser, FunnelWrapper funnelWrapper) {
        List<Map<String, Object>> realtimeCallListMap = realtimeCallMapper.selectMaps(Wrappers.<RealtimeCall>lambdaQuery().between(RealtimeCall::getCreatedAt, DateUtil.beginOfDay(DateUtil.date()), DateUtil.endOfDay(DateUtil.date())).orderByDesc(RealtimeCall::getId));
        List<Long> callIds = realtimeCallListMap.stream().map(real -> MapUtil.getLong(real, "id")).collect(Collectors.toList());
        List<Ride> rides = rideMapper.selectList(Wrappers.<Ride>lambdaQuery().in(Ride::getRefId, callIds).in(Ride::getState, Arrays.asList(RideStateState.ended, RideStateState.completed, RideStateState.marked)));

        Integer orderCancel = rideMapper.selectCount(Wrappers.<Ride>lambdaQuery().eq(Ride::getState, RideStateState.cancelled).between(Ride::getCreatedAt, DateUtil.beginOfDay(DateUtil.date()), DateUtil.endOfDay(DateUtil.date())));
        int sendNum = driverConfirmCallPersistedMapper.selectCountSendNum(callIds,0);
        int joinCount = driverGrabRealtimePersistedMapper.selectCountJoinDriver(callIds);
        int callNum = CollUtil.isNotEmpty(callIds) ? callIds.size() : 0;
        int completeNum = CollUtil.isNotEmpty(rides) ? rides.size() : 0;
        int canclNum = orderCancel != null ? orderCancel.intValue() : 0;

        Map<String, Object> result = new HashMap<>();
        result.put("callNum", callNum);
        result.put("completeNum", completeNum);
        result.put("completeRate", NumberUtil.formatPercent(NumberUtil.div(completeNum, callNum), 2));
        result.put("sendNum", sendNum);
        result.put("avgSendDriver", sendNum / callNum);
        result.put("joinCount", joinCount);
        result.put("avgJoinDriver", NumberUtil.div(joinCount, callNum, 1));
        result.put("canclNum", canclNum);
        return RestResult.success(result);
    }

    @Override
    public RestResult<?> getCapacityData(AdminUser adminUser, FunnelWrapper funnelWrapper) {

        int inServiceNum = driverMapper.selectCount(Wrappers.<Driver>lambdaQuery().eq(Driver::getInService, true));
        int taxiNum = taxiMapper.selectCount(null);
        List<String> joinDriverIds = funnelMapper.selectJoinDriver(DateUtil.beginOfDay(DateUtil.date()), DateUtil.endOfDay(DateUtil.date()));

        List<String> replyDriverIds = joinDriverIds.stream().flatMap(ids -> Arrays.stream(ids.split(","))).distinct().collect(Collectors.toList());


        List<Map<String, Object>> rideOrders = funnelMapper.selectDoneOrder(funnelWrapper);

        int completeDriverNum = rideOrders.stream().map(ride -> MapUtil.getLong(ride, "driverId")).distinct().collect(Collectors.toList()).size();


        Map<String, Object> result = new HashMap<>();
        result.put("inServiceNum", inServiceNum);
        result.put("inServiceRate", NumberUtil.formatPercent(NumberUtil.div(inServiceNum, taxiNum), 2));
        result.put("replyNum", replyDriverIds.size());
        result.put("completeDriverNum", completeDriverNum);
        result.put("completeDriverRate", NumberUtil.formatPercent(NumberUtil.div(completeDriverNum, inServiceNum), 2));
        return RestResult.success(result);
    }

    @Override
    public RestResult<?> getNoReplyList(AdminUser adminUser, FunnelWrapper funnelWrapper) {
        Page page = new Page<>(funnelWrapper.getCurrent(), funnelWrapper.getSize());
        funnelWrapper.setStartDate(DateUtil.beginOfDay(DateUtil.parseDate(funnelWrapper.getDateTime())));
        funnelWrapper.setEndDate(DateUtil.endOfDay(DateUtil.parseDate(funnelWrapper.getDateTime())));
        Page<Map<String, Object>> resultMap = funnelMapper.selectNoreply(page, funnelWrapper);
        return RestResult.success(resultMap);
    }

    @Override
    public RestResult<?> getCancelList(AdminUser adminUser, FunnelWrapper funnelWrapper) {
        funnelWrapper.setStartDate(DateUtil.beginOfDay(DateUtil.parseDate(funnelWrapper.getDateTime())));
        funnelWrapper.setEndDate(DateUtil.endOfDay(DateUtil.parseDate(funnelWrapper.getDateTime())));
        List<CancelDataVO> resultMap = funnelMapper.getCancelList(funnelWrapper);
        List<CancelDataVO> list = RAMPageUtil.page(resultMap, funnelWrapper.getCurrent(), funnelWrapper.getSize());


        IPage<CancelDataVO> page = new Page<>();
        page.setRecords(list);
        page.setPages(RAMPageUtil.getPages(resultMap.size(), funnelWrapper.getSize()));
        page.setTotal(resultMap.size());
        page.setCurrent(funnelWrapper.getCurrent());
        page.setSize(funnelWrapper.getSize());

        List<CancelDataVO> adminCancel = resultMap.stream().filter(e -> e.getTriggerBy() == 3).collect(Collectors.toList());
        List<CancelDataVO> userCancel = resultMap.stream().filter(e -> e.getTriggerBy() == 1).collect(Collectors.toList());
        List<CancelDataVO> driverCancel = resultMap.stream().filter(e -> e.getTriggerBy() == 2).collect(Collectors.toList());
        List<CancelDataVO> systemCancel = resultMap.stream().filter(e -> e.getTriggerBy() == -1).collect(Collectors.toList());

        Map<String, Object> map = new HashMap<>();
        map.put("page", page);
        map.put("adminCancel", CollUtil.isNotEmpty(adminCancel) ? adminCancel.size() : 0);
        map.put("userCancel", CollUtil.isNotEmpty(userCancel) ? userCancel.size() : 0);
        map.put("driverCancel", CollUtil.isNotEmpty(driverCancel) ? driverCancel.size() : 0);
        map.put("systemCancel", CollUtil.isNotEmpty(systemCancel) ? systemCancel.size() : 0);

        return RestResult.success(map);
    }

    @Override
    public RestResult<?> getSendDrivers(AdminUser adminUser, FunnelWrapper funnelWrapper) {
        List<Map<String, Object>> sends = funnelMapper.getSendDrivers(funnelWrapper.getCallId());
        return RestResult.success(sends);
    }

    @Override
    public RestResult<?> getGrabDrivers(AdminUser adminUser, FunnelWrapper funnelWrapper) {
        DriverGrabRealtimePersisted grab = driverGrabRealtimePersistedMapper.selectOne(Wrappers.<DriverGrabRealtimePersisted>lambdaQuery()
                .eq(DriverGrabRealtimePersisted::getRealtimeCallId, funnelWrapper.getCallId()));
        if (ObjectUtil.isNotNull(grab)) {
            String[] split = grab.getJoinedDriverIds().replace("[", "").replace("]", "").split(",");
            return RestResult.success(funnelMapper.getGrabDrivers(Arrays.asList(split)));
        }
        return RestResult.success(new ArrayList<>());
    }

    @Override
    public RestResult<?> getCountToDayRide(Map<String, Object> params) {
        Integer area = MapUtil.getInt(params, "area");
        String type = MapUtil.getStr(params, "type");

        int sendNum = 0;
        int joinCount = 0;
        int outCarNum = 0;
        int callNum = 0;
        int completeNum = 0;
        if ("real_time".equals(type)) {
            List<Map<String, Object>> realtimeCallListMap = realtimeCallMapper.selectMaps(Wrappers.<RealtimeCall>lambdaQuery()
                    .between(RealtimeCall::getCreatedAt, DateUtil.beginOfDay(DateUtil.date()), DateUtil.endOfDay(DateUtil.date()))
                    .orderByDesc(RealtimeCall::getId));
            List<Long> callIds = realtimeCallListMap.stream().map(real -> MapUtil.getLong(real, "id")).collect(Collectors.toList());
            if (CollUtil.isNotEmpty(callIds)) {
                completeNum = rideMapper.selectCount(Wrappers.<Ride>lambdaQuery()
                        .in(Ride::getRefId, callIds)
                        .in(Ride::getState, Arrays.asList(RideStateState.ended, RideStateState.completed, RideStateState.marked)));
                sendNum = driverConfirmCallPersistedMapper.selectCountSendNum(callIds,0);
                joinCount = driverGrabRealtimePersistedMapper.selectCountJoinDriver(callIds);
                callNum = callIds.size();
            }
            outCarNum = taxiMapper.selectCount(Wrappers.<Taxi>lambdaQuery().in(Taxi::getIsAvailable, 1));
        } else if ("phone".equals(type)) {


        } else if ("help_old".equals(type)) {

        }

        Map<String, Integer> result = new HashMap<>();
        result.put("callNum", callNum);
        result.put("completeNum", completeNum);
        result.put("sendNum", sendNum);
        result.put("joinCount", joinCount);
        result.put("outCarNum", ObjectUtil.isNull(outCarNum) ? 0 : outCarNum);
        return null;
    }
}